﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Xml.Serialization;
using OAuthLib;
using SteroMood.StereoMood_Data;

namespace SteroMood
{
    public static class StereoMoodClient
    {
        public enum StereomoodType
        {
            Mood,
            Activity,
            Selected,
            Site
        }
        private static Consumer _consumer = null;
        private static RequestToken _requestToken = null;
        private static AccessToken _accessToken = null;
        public static bool Connected { get; private set; }
        public static string LastError { get; private set; }

        public static int RateLimit { get; private set; }

        #region Authentication
        public static bool AuthenticatePhaseOne()
        {
            if (Connected)
            {
                return true;
            }
            try
            {
                if (Directory.Exists(AssemblyCacheDirectory) == false)
                {
                    Directory.CreateDirectory(AssemblyCacheDirectory);
                }

                RateLimit = 0;
                _consumer = new Consumer("01d7712693e4b522c6e00c4c66a76fdb04df9b8f4", "b39792a542b57b83333c9e5f1aad5666");
                _requestToken = _consumer.ObtainUnauthorizedRequestToken("http://www.stereomood.com/api/oauth/request_token", "http://stereomood.com/");

                Process.Start(
                    Consumer.BuildUserAuthorizationURL(
                        "http://www.stereomood.com/api/oauth/authorize",
                        _requestToken
                    )
                );
                return true;
            }
            catch (Exception ex)
            {
                LastError = "AuthenticatePhaseOne, " + ex.Message;
                return false;
            }
        }
        public static bool AuthenticatePhaseTwo(string pincode)
        {
            if (Connected)
            {
                return true;
            }

            try
            {
                //Console.Out.WriteLine("Input verifier");
                String verifier = pincode;
                verifier = verifier.TrimEnd('\r', '\n');
                _accessToken = _consumer.RequestAccessToken(verifier, _requestToken, "http://www.stereomood.com/api/oauth/access_token", "http://stereomood.com/");
                Connected = true;
                return true;
            }
            catch (Exception ex)
            {
                LastError = "AuthenticatePhaseTwo, " + ex.Message;
                return false;
            }
        }
        #endregion

        #region Generic query on stereomood
        /// <summary>
        /// Generic query on stereomood
        /// </summary>
        /// <typeparam name="T">object to serialize in response to the query</typeparam>
        /// <param name="query">url to query</param>
        /// <param name="parameters">parameter of the query</param>
        /// <param name="result">serialized response typed T</param>
        /// <returns>true if succeeded, false if not</returns>
        private static bool QueryStereoMood<T>(string query, Parameter[] parameters, ref T result) where T : class
        {
            try
            {

                string key = query;
                foreach (Parameter parameter in parameters)
                {
                    key += string.Format("[{0}][{1}],", parameter.Name, parameter.Value);
                }

                if(DeserializeObject<T>(key, out result) )
                {
                    return true;
                }


                RateLimit++;
                HttpWebResponse resp = _consumer.AccessProtectedResource(_accessToken, query, "GET", "http://stereomood.com/", parameters);
                if (resp != null)
                {
                    if (resp.StatusCode == HttpStatusCode.BadRequest)
                    {
                        Debug.Assert(false);
                    }
                    Stream responseStream = resp.GetResponseStream();
                    if (responseStream != null)
                    {
                        // Load the object saved above by using the Deserialize function
                        XmlSerializer serializerObj = new XmlSerializer(typeof(T));
                        T loadedObj = (T)serializerObj.Deserialize(responseStream);

                        // Add result to cache
                        SerializeObject<T>(loadedObj, key);

                        result = loadedObj;
                        return true;
                    }
                }
            }
            catch (Exception ex)
            {
                LastError = "QueryStereoMood Query: " + query + " Ex: " + ex.Message;
            }
            return false;
        }

        static public string AssemblyDirectory
        {
            get
            {
                string codeBase = Assembly.GetExecutingAssembly().CodeBase;
                UriBuilder uri = new UriBuilder(codeBase);
                string path = Uri.UnescapeDataString(uri.Path);
                return Path.GetDirectoryName(path);
            }
        }


        static public string AssemblyCacheDirectory
        {
            get { return Path.Combine(AssemblyDirectory, "Cache"); }
        }

        private static bool SerializeObject<T>(T obj, string key) where T : class
        {
            try
            {
                string invalid = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
                key = invalid.Aggregate(key, (current, c) => current.Replace(c.ToString(), ""));
                key = Path.Combine(AssemblyCacheDirectory, key+".xml");

                // Create a new XmlSerializer instance with the type of the test class
                XmlSerializer serializerObj = new XmlSerializer(typeof(T));
                TextWriter writeFileStream = new StreamWriter(key);
                serializerObj.Serialize(writeFileStream, obj);
                writeFileStream.Close();
                return true;
            }
            catch (Exception ex)
            {
                LastError = "Serialize Object " + ex.Message;
            }
            return false;
        }


        private static bool DeserializeObject<T> (string key, out T obj) where T : class
        {
            obj = null;
            try
            {
                string invalid = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
                key = invalid.Aggregate(key, (current, c) => current.Replace(c.ToString(), ""));
                key = Path.Combine(AssemblyCacheDirectory, key + ".xml");

                if(File.Exists(key)==false)
                {
                    return false;
                }

                // Create a new XmlSerializer instance with the type of the test class
                XmlSerializer SerializerObj = new XmlSerializer(typeof(T));

                // Create a new file stream for reading the XML file
                FileStream ReadFileStream = new FileStream(key, FileMode.Open, FileAccess.Read, FileShare.Read);

                // Load the object saved above by using the Deserialize function
                obj = (T)SerializerObj.Deserialize(ReadFileStream);
                ReadFileStream.Close();
                return true;
            }
            catch (Exception ex)
            {
                LastError = "Serialize Object " + ex.Message;
            }
            return false;
        }

        #endregion

        #region Playlists
        public static Playlists GetPlaylists(string user)
        {
            Playlists playlists = null;
            if (QueryStereoMood(Playlists.UrlList, new[] { new Parameter("user_name", user), new Parameter("limit", "100") }, ref playlists))
            {
                return playlists;
            }
            return null;
        }
        #endregion
        
        #region Tags
        public static Tags GetTopTags()
        {
            return GetTags(Tags.UrlTop);
        }
        public static Tags GetSelectedMoodTags()
        {
            return GetTags(Tags.UrlSelectedMood);
        }
        public static Tags GetSelectedActivityTags()
        {
            return GetTags(Tags.UrlSelectedActivity);
        }
        private static Tags GetTags(string url)
        {
            Tags tags = null;
            if (QueryStereoMood(url, new []{new Parameter("limit", "100"), new Parameter("page", "1")}, ref tags))
            {

                return tags;
            }
            return null;
        }
        #endregion

        #region Search
        public static Search SearchSongs(string type, string query)
        {
            Search results = new Search();
            results.PageIndex = 0;
            results.TotalPages = -1;
            do
            {
                results.PageIndex++;
                Search search = null;
                if (QueryStereoMood(Search.Url, new[] { new Parameter("q", query), new Parameter("type", type), new Parameter("limit", "100"), new Parameter("page", results.PageIndex.ToString()) }, ref search))
                {
                    results.TotalPages = search.TotalPages;
                    results.TotalTrackNumber = search.TotalTrackNumber;
                    results.PageIndex = search.PageIndex;
                    results.SongsContainer.SongsContainer.AddRange(search.SongsContainer.SongsContainer);
                }
                else
                {
                    break;
                }
            } while (results.PageIndex != results.TotalPages);

            if (results.PageIndex != 0)
            {
                return results;
            }
            return null;
        }
        #endregion
    }
}
